﻿using Inovout;
using Newtonsoft.Json;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Web;

namespace System.Web.Mvc
{
    public class ChartInfo<TValue>
    {
        public ChartInfo()
        {
            datas = new List<object>();
        }
        private List<object> datas;
        public string Datas
        {
            get { return JsonConvert.SerializeObject(datas); }
        }
        public void AddData(string name, IEnumerable<TValue> data)
        {
            datas.Add(new { name = name, data = data });
        }
        protected void AddData(string name, object data)
        {
            datas.Add(new { name = name, data = data });
        }
        public string YCategories { get; private set; }
        internal void SetYCategories(IEnumerable yCategoriesp)
        {
            this.YCategories = JsonConvert.SerializeObject(yCategoriesp);
        }
    }
    public class ChartInfo<TX, TY> : ChartInfo<TY>
    {
        public decimal FloatRate { get; set; }
        public ChartInfo()
            : this(0.1M)
        {
        }
        public ChartInfo(decimal floatRate)
            : base()
        {
            this.FloatRate = floatRate;
        }
        public KeyValuePair<TX, TY>[] PlotLines { get; private set; }
        internal void SetPlotLines(KeyValuePair<TX, TY>[] plotLines)
        {
            this.PlotLines = plotLines;
            SetMinAndMax(plotLines.Select(kv => kv.Key), plotLines.Select(kv => kv.Value));
        }
        private static readonly DateTime GregorianCalendar = DateTime.Parse("1970-1-1");


        public void AddData(string name, IEnumerable<KeyValuePair<TX, TY>> data)
        {
            object item = data;
            if (typeof(TX) == typeof(DateTime))
            {
                item = data.Select(kv => new object[] {
                              (long)((DateTime)(object)kv.Key).Subtract(GregorianCalendar).TotalMilliseconds, kv.Value });
            }
            else if (typeof(TX) == typeof(DateTimeRange))
            {
                item = data.Select(kv => new object[] {
                              (long)((DateTimeRange)(object)kv.Key).Start.Subtract(GregorianCalendar).TotalMilliseconds, kv.Value });
            }
            else
            {
                item = data.Select(kv => new object[] { kv.Key, kv.Value });
            }
            AddData(name, item);
            SetMinAndMax(data.Select(kv => kv.Key), data.Select(kv => kv.Value));
        }
        private TypeConverter typeConverter = new TypeConverter();

        public TX MinX { get; private set; }
        public TX MaxX { get; private set; }
        private void SetMinAndMax(IEnumerable<TX> xValues, IEnumerable<TY> yValues)
        {
            TX minXValue = xValues.Min();
            TX maxXValue = xValues.Max();

            if ( DefaultValue.Is(this.MinX) || Comparer<object>.Default.Compare(this.MinX, minXValue) > 0)
            {
                this.MinX = minXValue;
            }
            if (DefaultValue.Is(this.MinX) || Comparer<object>.Default.Compare(this.MaxX, maxXValue) < 0)
            {
                this.MaxX = maxXValue;
            }

            TY minYValue = yValues.Min();
            TY maxYValue = yValues.Max();

            if (DefaultValue.Is(this.MinY) || Comparer<object>.Default.Compare(this.MinY, minYValue) > 0)
            {
                this.MinY = GenericCast((1M - this.FloatRate) * decimal.Parse(minYValue.ToString()));
            }
            if (DefaultValue.Is(this.MinY) || Comparer<object>.Default.Compare(this.MaxY, maxYValue) < 0)
            {
                this.MaxY = GenericCast((1 + this.FloatRate) * decimal.Parse(maxYValue.ToString()));
            }
        }
        private TY GenericCast(decimal value)
        {
            return GenericTypeConvert.Cast<decimal, TY>(value);
        }
        public TY MinY { get; private set; }
        public TY MaxY { get; set; }
    }
    public class GenericTypeConvert
    {
        public static TDestination Cast<TSource, TDestination>(TSource value)
        {
            object destinationValue = (object)value;
            IConvertible convertible = value as IConvertible;
            if (convertible != null)
            {

                switch (Type.GetTypeCode(typeof(TDestination)))
                {
                    case TypeCode.Boolean:
                        destinationValue = convertible.ToBoolean(null);
                        break;
                    case TypeCode.Byte:
                        destinationValue = convertible.ToByte(null);
                        break;
                    case TypeCode.Char:
                        destinationValue = convertible.ToChar(null);
                        break;
                    case TypeCode.Decimal:
                        destinationValue = convertible.ToDecimal(null);
                        break;
                    case TypeCode.Double:
                        destinationValue = convertible.ToDouble(null);
                        break;
                    case TypeCode.DateTime:
                        destinationValue = convertible.ToDateTime(null);
                        break;
                    case TypeCode.Int32:
                        destinationValue = convertible.ToInt32(null);
                        break;
                    default:
                        break;
                }
            }
            return (TDestination)destinationValue;

        }

    }
}